package com.javasea.scene.repeatSubmit.example2.lock;

import com.javasea.scene.repeatSubmit.example2.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Author hhl
 * @Date 2020/11/10 0010 18:43
 * @Version 1.0
 */
@Slf4j
@Service
public class RedisLock {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private StringRedisTemplate template;

    private static String LOCK_PREFIX = "prefix";

    /**
     * 该加锁方法仅针对单实例 Redis 可实现分布式加锁
     * 对于 Redis 集群则无法使用
     *
     * bug: 当两次操作的间隔在0.01秒内时候可能导致同时获取到锁！！！
     *
     * @param lockKey   加锁键
     * @param clientId  加锁客户端唯一标识(采用UUID)
     * @param seconds   锁过期时间
     * @return
     */
    public boolean tryLock(String lockKey, String clientId, long seconds) {
        ValueOperations<String, String> opsForValue = template.opsForValue();
        if (opsForValue.get(lockKey) == null) {
            opsForValue.set(lockKey, "0", seconds, TimeUnit.SECONDS);
            log.warn("设置成功+++++，说明加锁成功");
            return true;
        } else {
            log.warn("设置失败-----，说明加锁失败");
            return false;
        }

//        Object isExits = redisUtil.get(lockKey);
//        if (Objects.isNull(isExits)) {
//            redisUtil.set(lockKey, clientId, seconds);
//            return true;
//        } else {
//            return false;
//        }
    }

    /**
     * 暂时无并发问题
     * @param lockKey
     * @param clientId
     * @param seconds
     * @return
     */
    public boolean tryLock2(String lockKey, String clientId, long seconds){
        // 添加 redisson 依赖后将不支持 ，会报错ClassNotFoundException RedisStreamCommands
        BoundValueOperations boundValueOperations = template.boundValueOps(lockKey);
        Boolean flag = boundValueOperations.setIfAbsent("1", Duration.ofSeconds(10_000L));

        // 添加 redisson 依赖后将不支持 ，会报错ClassNotFoundException RedisStreamCommands
//        ValueOperations<String, String> stringStringValueOperations = template.opsForValue();
        // flag 表示的是否set。setIfAbsent采用的是redis的SETNX, setnx分布式锁存在单点分险，如果分布式锁挂掉的话，
        // 会丢锁导致多个客户端同时持有锁
        // 这里设置为-1，那么redis的TTL就是-1，如果Redisson设置-1，那么默认是30s，会自动续期
//        Boolean flag = stringStringValueOperations.setIfAbsent(lockKey, "1", Duration.ofSeconds(-1L));
        if(flag){
            log.warn("设置成功+++++，说明加锁成功");
        }else{
            log.warn("设置失败-----，说明加锁失败");
        }
        return flag;
    }

    /**
     * 与 tryLock 相对应，用作释放锁
     *
     * @param lockKey
     * @param clientId
     * @return
     */
    public boolean releaseLock(String lockKey, String clientId) {
        if (lockKey != null && lockKey.trim() != "" && clientId != null && clientId.trim() != "" ) {
            this.template.delete(lockKey);
//            redisUtil.remove(lockKey);
            return true;
        } else {
            return false;
        }
    }

}
